package com.jt.demo2.aop;
//AOP 面向切面编程
//切面 = 动态代理+方法的扩展  后期被AOP API封装

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executors;

@Component  //将这个类交给Spring容器管理
@Aspect     //标志当前类是一个切面 核心为动态代理
@Order(value = 1) //数字越小，越先执行
public class TxAspect {

    //编码：切面 = 切入点表达式 + 通知方法
    /**切入点表达式：配置AOP为谁创建对象！！*/
//    @Pointcut("bean(userServiceImpl)")  //该标签和实现类绑定 此时通过id创建了代理对象
                                         // 只能匹配固定的类/对象一个
//    @Pointcut("within(包名.类名)")
//    @Pointcut("within(com.jt.demo2.service.UserService)")   //一个
//    @Pointcut("within(com.jt.demo2.service.*)")             //多个 通配符*
//    @Pointcut("execution(int com.jt.demo2.service.UserServiceImpl.addUser())")
//    @Pointcut("execution(void com.jt.demo2.service.UserServiceImpl.addUser())")
    @Pointcut("@annotation(com.jt.demo2.anno.TX)")
    public void pointCut(){
        /**切入点表达式为方法的好处：可以将切入点表达式提取共性*/
    }
    //通知方法：对原有方法的扩展，通知必须与
    //    作用：当用户满足了切入点表达式，才会执行扩展方法
    /**
     * 通知类型
     *   1.@Before          前置通知：在目标方法运行前执行
     *   2.@afterReturning  后置通知：在目标方法执行之后执行
     *   3.@afterThrowing   异常通知：目标方法执行时，抛出异常时，异常通知执行
     *   4.@after       最终通知：不管目标方法执行是否正确，都要执行，一般不用，用来记录   与finally类似
     *   5.环绕通知：前四大通知类型不能控制目标方法的运行，所以在使用时一般记录程序的运行状态
     *          在目标方法执行前后，都要运行，只有环绕通知才可以控制目标方法是否运行，是使用最多的通知方法
     * */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("AOP前置通知");
        //1.获取目标对象的类型
        Class<?> targetClass = joinPoint.getTarget().getClass();
        //2.获取目标对象名称
        String targerName = joinPoint.getSignature().getDeclaringTypeName();
        //3.获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        //4.获取参数数据
        Object[] args = joinPoint.getArgs();
        System.out.println("目标对象类型："+targetClass);
        System.out.println("目标对象名称："+targerName);
        System.out.println("目标方法名称："+methodName);
        System.out.println("参数的名称"+ Arrays.toString(args));
    }
    /**
     * 后置通知可能会记录目标方法执行后的返回值结果
     * returning:后置通知获取返回值的属性
     * */
    @AfterReturning(value = "pointCut()",returning = "result")
    public void  afterReturning(Object result){
        System.out.println("AOP后置通知");
        System.out.println("方法的返回值"+result);
    }

    /**
     * 说明：如果程序执行抛出的一场，则可以由异常通知进行记录
     *    throwing：抛出异常的属性
     * */
    /*注意！！JoinPoint和ProceedingJoinPoint参数必须放在列表第一位！*/
    @AfterThrowing(value = "pointCut()",throwing ="exception")
    public void afterThrow(JoinPoint joinPoint,Exception exception){
        exception.printStackTrace();  //打印异常数据
        System.out.println("抛出异常！！！"+exception.getMessage());//打印异常信息

    }
    @After("pointCut()")
    public void after(){
        System.out.println("我是最终的通知！！");
    }
    /**
     * 注意事项：
     *     环绕通知中必须添加参数，并且参数ProceedingJoinPoint必须位于第一位
     * 用法：
     *     Proceed with the next advice or target method invocation
     *   1.如果有下一个通知，则执行下一个通知
     *   2.如果没有下一个通知，则执行目标方法
     *
     * */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知开始");
        Object result = joinPoint.proceed();
        System.out.println("环绕通知结束");
        return result;
    }

}
